package de.mouseless.csv2sql.parsers;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import de.mouseless.csv2sql.common.ApplicationException;
import de.mouseless.csv2sql.model.AbstractColumn;
import de.mouseless.csv2sql.model.CharColumn;
import de.mouseless.csv2sql.model.DataType;
import de.mouseless.csv2sql.model.DefaultColumn;
import de.mouseless.csv2sql.model.ForeignKeyConstraint;
import de.mouseless.csv2sql.model.NumberColumn;
import de.mouseless.csv2sql.model.UniqueKeyConstraint;

public class ColumnParser extends AbstractParser {
   
   private static final Map<String, DataType> TYPES = new HashMap<String, DataType>();

   private final List<String> line;

   public ColumnParser(List<String> line) {
      this.line = line;
   }

   public void parseColumn(ParserContext context) {
      String columnName = line.get(0).toUpperCase();
      if(line.size()<2) {
         throw new ApplicationException("At least two arguments neccessary for defining column " + columnName);
      }
      //Determine column type
      determineColumn(context, columnName);
      
      determineFK(context, columnName);
      
      determineUC(context, columnName);
   }

   private void determineUC(ParserContext context, String columnName) {
      int ucIndex = line.indexOf("uc");
      if(ucIndex!=-1) {
         context.getModel().addConstraint(new UniqueKeyConstraint(context.getCurrentTable().getName() + "_UC"));
      }
   }

   private void determineFK(ParserContext context, String columnName) {
      int fkIndex = line.indexOf("fk");
      if(fkIndex!=-1) {
         String toTableName = null;
         String toColName = null;
         
         //Determine referTo table / col
         if(line.size()<fkIndex+2) {
            //determine referTo table from name as referToTableName_referToColName
            if(!columnName.contains("_")) {
               throw new ApplicationException("FK could not be derived from col name " + columnName);
            }
            String[] parts = columnName.split("_");
            toTableName = parts[0];
            toColName = parts[1];
         }else {
            //referTo Tablename/ colname in next token in form tablename.colname
            String fkInfo = line.get(fkIndex+1);
            if(!fkInfo.contains(".")) {
               toTableName = fkInfo;
               toColName = "ID";
            }else {
               String[] parts = fkInfo.split("\\.");
               toTableName = parts[0];
               toColName = parts[1];
            }
         }
         
         //Determine Name
         String fkName = null;
         String fromTableName = context.getCurrentTable().getName();
         if(line.size()<fkIndex+3) {
            //no name specified --> derive from table names
            fkName = cutTo(fromTableName, 12) + "_" + cutTo(toTableName, 12) + "_FK"; 
         }else {
            fkName = line.get(fkIndex+3);
         }
         
         //Add to model
         context.getModel().addConstraint(new ForeignKeyConstraint(fkName, 
              fromTableName, columnName, toTableName, toColName));
      }
   }

   private void determineColumn(ParserContext context, String columnName) {
      AbstractColumn column = null;
      boolean nullable = !line.contains("nn");
      if(!line.get(1).toLowerCase().equals("fk")) {
         String type = line.get(1).toLowerCase();
         if(!TYPES.containsKey(type)) {
            throw new ApplicationException("Unkown type for column " + columnName);
         }
         
         DataType dataType = TYPES.get(type);
         
         if(dataType==DataType.CHAR || dataType==DataType.VARCHAR2) {
            if(line.size()<3) {
               throw new ApplicationException("Length is missing for col definition " + columnName);
            }
            column = new CharColumn(columnName, new Integer(line.get(2)), nullable);
            column.setType(dataType);
         }else if(dataType == DataType.NUMBER) {
            if(line.size()<3) {
               throw new ApplicationException("Precision/scale is missing for col definition " + columnName);
            }
            String precScaleString = line.get(2);
            Integer precision = null;
            Integer scale = 0;
            if(precScaleString.contains(",")) {
               String[] parts = precScaleString.split("\\s*,\\s*");
               precision = new Integer(parts[0]);
               scale = new Integer(parts[1]);
            }else {
               precision = new Integer(precScaleString);
            }
            column = new NumberColumn(columnName, nullable, precision, scale);
            column.setType(dataType);
         }else if(dataType == DataType.DATE || dataType == DataType.BLOB || dataType == DataType.CLOB) {
            column = new DefaultColumn(columnName, nullable, dataType);
         }            
      }
      context.getCurrentTable().addColumn(column);
   }
   
   static {
      TYPES.put("c", DataType.CHAR);
      TYPES.put("char", DataType.CHAR);
      TYPES.put("v", DataType.VARCHAR2);
      TYPES.put("varchar", DataType.VARCHAR2);
      TYPES.put("varchar2", DataType.VARCHAR2);
      TYPES.put("n", DataType.NUMBER);
      TYPES.put("nu", DataType.NUMBER);
      TYPES.put("number", DataType.NUMBER);
      TYPES.put("d", DataType.DATE);
      TYPES.put("date", DataType.DATE);
      TYPES.put("b", DataType.BLOB);
      TYPES.put("bl", DataType.BLOB);
      TYPES.put("blob", DataType.BLOB);
      TYPES.put("cl", DataType.CLOB);
      TYPES.put("clob", DataType.CLOB);
   }

}
